package com.yvan.leto.server.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.yvan.Conv;
import com.yvan.YvanUtil;
import com.yvan.leto.ResourceFoundException;
import com.yvan.platform.JsonWapper;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.List;
import java.util.Set;

@Slf4j
public class FileUtils {

    private static final ObjectMapper objectMapper;
    private static final ObjectMapper objectYamlMapper;

    static {
        objectMapper = YvanUtil.objectMapper;
        objectYamlMapper = YvanUtil.objectYamlMapper;
    }

    /**
     * 规范相对路径
     */
    public static String normalizePath(Object... paths) {
        if (paths.length == 0) {
            return "";
        }
        List<String> fs = Lists.newArrayList();
        for (Object obj : paths) {
            if (obj instanceof String) {
                fs.add((String) obj);
            } else if (obj instanceof List) {
                for (Object v : (List) obj) {
                    fs.add(Conv.NS(v));
                }
            } else if (obj instanceof String[]) {
                for (String v : (String[]) obj) {
                    fs.add(v);
                }
            } else if (obj instanceof Object[]) {
                for (Object v : (Object[]) obj) {
                    fs.add(Conv.NS(v));
                }
            }
        }

        final String path = Joiner.on("/").join(fs);
        List<String> vs = Splitter.on('/').omitEmptyStrings().splitToList(path.replace("\\", "/"));
        return Joiner.on("/").join(vs);
    }

    /**
     * 根据环境，获取一组文件的所有组合形式，并判断是否存在
     */
    public static List<File> getEnvOfResource(String envs, Object... paths) {
        List<File> resourceList = Lists.newArrayList();
        val fileRaw = getEnvResource("", paths);
        if (fileRaw != null && fileRaw.exists()) {
            resourceList.add(fileRaw);
        }
        val envArray = Splitter.on(",").omitEmptyStrings().splitToList(envs);
        for (String env : envArray) {
            val file = getEnvResource(env, paths);
            if (file != null && file.exists()) {
                resourceList.add(file);
            }
        }
        return resourceList;
    }

    /**
     * 根据环境和文件名获取个文件
     * dev, /path/of/file_name.yml -> /path/of/file_name-dev.yml
     * '', /path/of/file_name.yml -> /path/of/file_name.yml
     * dev, /path/of/file_name -> /path/of/file_name-dev
     * '', /path/of/file_name -> /path/of/file_name
     */
    public static File getEnvResource(String env, Object... fileNameRawObj) {
        if (Strings.isNullOrEmpty(env)) {
            return getResourceQuietly(fileNameRawObj);
        }

        List<String> filePaths = Splitter.on("/").omitEmptyStrings().splitToList(
                normalizePath(fileNameRawObj)
        );
        if (filePaths.size() <= 0) {
            return new File(normalizePath(fileNameRawObj));
        }
        val fileNameRaw = filePaths.get(filePaths.size() - 1);

        val dot = fileNameRaw.lastIndexOf('.');
        String ext, fn;
        if ((dot > -1) && (dot < (fileNameRaw.length() - 1))) {
            ext = fileNameRaw.substring(dot + 1).toLowerCase();
            fn = fileNameRaw.substring(0, dot);
        } else {
            //文件没有扩展名
            ext = "";
            fn = fileNameRaw;
        }

        Object[] filePathArray = new Object[filePaths.size()];
        filePaths.toArray(filePathArray);
        //最后一截文件，改名
        filePathArray[filePaths.size() - 1] = fn + "-" + env + (Strings.isNullOrEmpty(ext) ? "" : "." + ext);
        return getResourceQuietly(filePathArray);
    }

    /**
     * 获取 File 对象，如果 File 不存在 则抛出 ResourceFoundException 异常
     */
    @SneakyThrows
    public static File getResource(Object... paths) {
        val file = new File(normalizePath(paths));
        log.debug("read {}", file.getCanonicalPath());
        if (!file.exists()) {
            throw new ResourceFoundException(file.getCanonicalPath());
        }
        if (!file.canRead()) {
            throw new ResourceFoundException(file.getCanonicalPath(), true);
        }
        return file;
    }

    /**
     * 获取 File 对象，并准备进入写入状态。如果不允许写入，则抛出异常
     */
    @SneakyThrows
    public static File getWriteFile(Object... paths) {
        val file = new File(normalizePath(paths));
        log.debug("write {}", file.getCanonicalPath());
        return file;
    }

    /**
     * 获取 File 对象，但并不校验文件是否存在，或是否允许读写
     */
    @SneakyThrows
    public static File getResourceQuietly(Object... paths) {
        val file = new File(normalizePath(paths));
        log.debug("qtryread {}", file.getCanonicalPath());
        return file;
    }

    /**
     * 读取 yaml 文件，并进行对象转换
     */
    @SneakyThrows
    public static <T> T readYaml(Class<T> clazz, Object... location) {
        val file = getResource(location);
        @Cleanup InputStream is = new FileInputStream(file);
        return objectYamlMapper.readValue(is, clazz);
    }

    /**
     * 写入 yaml 文件
     */
    @SneakyThrows
    public static void writeYaml(Object object, Object... location) {
        val file = getWriteFile(location);
        objectYamlMapper.writerWithDefaultPrettyPrinter().writeValue(file, object);
    }

    @SneakyThrows
    public static void write(String content, File file) {
        YvanUtil.saveToFile(file.getCanonicalPath(), content);
    }

    /**
     * 读取 yaml 文件，并返回 json操作类
     */
    @SneakyThrows
    public static JsonWapper readYaml(Object... location) {
        val file = getResource(location);
        @Cleanup InputStream is = new FileInputStream(file);
        JsonNode jsonNode = objectYamlMapper.readTree(is);
        return new JsonWapper(jsonNode);
    }

    /**
     * 读取目标文件，并返回文件的所有内容，如果文件不存在，则抛出 ResourceFoundException 异常
     */
    public static String readContent(Object... location) {
        return readContent(getResource(location));
    }

    /**
     * 读取目标文件，并返回文件的所有内容
     */
    @SneakyThrows
    public static String readContent(File file) {
        if (!file.exists()) {
            return "";
        }
        @Cleanup InputStream is = new FileInputStream(file);
        List<String> lines = IOUtils.readLines(is, Charset.forName("UTF-8"));
        return Joiner.on("\n").join(lines);
    }

    /**
     * 列出某个文件夹下的所有文件（不包含目录）
     */
    public static File[] listFile(Object... paths) {
        val file = new File(normalizePath(paths));
        return file.listFiles((r) -> r.isFile());
    }

    /**
     * 列出某个文件夹下的所有 排除环境因素的文件（不包含目录）
     */
    public static Collection<String> listFileNoEnv(Object... paths) {
        val file = new File(normalizePath(paths));
        File[] files = file.listFiles((r) -> r.isFile());
        Set<String> r = Sets.newLinkedHashSet();
        for (File f : files) {
            if (f.getName().contains("-")) {
                String fn = f.getName().substring(0, f.getName().lastIndexOf("-")) + "." + FileUtils.getExtName(f.getName());
                r.add(fn);
            } else {
                r.add(f.getName());
            }
        }
        return r;
    }

    /**
     * 列出某个文件夹下的所有目录（不包含文件）
     */
    public static File[] listDirectory(Object... paths) {
        val file = new File(normalizePath(paths));
        return file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return !pathname.isFile();
            }
        });
    }

    public static boolean exists(Object... paths) {
        return getResourceQuietly(paths).exists();
    }

    @SneakyThrows
    public static boolean delete(File file) {
        log.debug("delete {}", file.getCanonicalPath());
        if (!file.exists()) {
            return false;
        }
        if (file.isDirectory()) {
            org.apache.commons.io.FileUtils.deleteDirectory(file);
            return true;
        } else {
            return file.delete();
        }
    }

    public static boolean delete(Object... paths) {
        return delete(getResourceQuietly(paths));
    }

    /**
     * 获取任何路径形式中的 路径片段
     * file/of/path = full_path
     */
    public static String getFullPath(Object... paths) {
        String fullName = normalizePath(paths);
        val fns = Splitter.on("/").omitEmptyStrings().splitToList(fullName);
        if (fns.size() <= 1) {
            return "";
        }
        return normalizePath(fns.subList(0, fns.size() - 1));
    }

    /**
     * 获取任何路径形式中的 文件名片段（不包含profile、不包含ext_name）
     * file/of/Path/fileName.ext_name = full_name
     * file/of/Path/fileName-profile.ext_name = full_profile_name
     * file/of/path = full_path
     * fileName-profile.ext_name = file_profile_name
     */
    public static String getFileName(Object... paths) {
        String fullName = normalizePath(paths);
        val fns = Splitter.on("/").omitEmptyStrings().splitToList(fullName);
        String fileProfileName = fns.get(fns.size() - 1);
        if (fileProfileName.contains("-")) {
            int pos = fileProfileName.lastIndexOf("-");
            return fileProfileName.substring(0, pos);
        }
        if (fileProfileName.contains(".")) {
            int pos = fileProfileName.lastIndexOf(".");
            return fileProfileName.substring(0, pos);
        }
        return fileProfileName;
    }

    /**
     * 根据任何路径形式，获取想要的 profile 的全名（包含 fullPath + profile + ext）
     * file/of/Path/fileName-profile.ext_name = full_profile_name
     */
    public static String getFullProfileName(String profile, Object... paths) {
        final String ext = getExtName(paths);
        return getFullPath(paths) + "/"
                + getFileName(paths)
                + (!Strings.isNullOrEmpty(profile) ? "-" + profile : "")
                + (!Strings.isNullOrEmpty(ext) ? "." + ext : "");
    }

    /**
     * 根据任何路径形式，获取想要的 file_profile_name （不包含 fullPath , 但包含 profile + file_name + ext）
     * fileName-profile.ext_name = file_profile_name
     */
    public static String getFileProfileName(String profile, Object... paths) {
        final String ext = getExtName(paths);
        return getFileName(paths)
                + (!Strings.isNullOrEmpty(profile) ? "-" + profile : "")
                + (!Strings.isNullOrEmpty(ext) ? "." + ext : "");
    }

    /**
     * 获取任何路径形式中的 扩展名片段（不包含profile、不包含ext_name）
     * file/of/Path/fileName.ext_name = full_name
     * file/of/Path/fileName-profile.ext_name = full_profile_name
     * file/of/path = full_path
     * fileName-profile.ext_name = file_profile_name
     */
    public static String getExtName(Object... paths) {
        String fullName = normalizePath(paths);
        val fns = Splitter.on("/").omitEmptyStrings().splitToList(fullName);
        String fileProfileName = fns.get(fns.size() - 1);
        int pos = fileProfileName.lastIndexOf('.');
        if (pos < 0) {
            return "";
        }
        return fileProfileName.substring(pos + 1).toLowerCase();
    }

    /**
     * 获取任何路径形式中的 profile 片段（不包含 path / file_name / ext_name）
     * file/of/Path/fileName-profile.ext_name = full_profile_name
     */
    public static String getProfileName(Object... paths) {
        String fullName = normalizePath(paths);
        val fns = Splitter.on("/").omitEmptyStrings().splitToList(fullName);
        String fileProfileName = fns.get(fns.size() - 1);
        if (!fileProfileName.contains("-")) {
            return "";
        }
        int pos = fileProfileName.lastIndexOf('-');
        int dot = fileProfileName.lastIndexOf('.');
        if (dot < 0) {
            return fileProfileName.substring(pos + 1);
        }
        return fileProfileName.substring(pos + 1, dot);
    }
/*
    public static void main(String[] args) {

        System.out.println(getExtName("file/of/Path/fileName.ext_name"));
        System.out.println(getExtName("file/of/Path/fileName"));
        System.out.println(getExtName("file/of/Path.abc/fileName.txt"));
        System.out.println(getExtName("fileName.ext_name"));
        System.out.println(getExtName("fileName"));
        System.out.println(getExtName("fileName.txt"));
        System.out.println(getExtName("file/of/Path/fileName-test.ext_name"));
        System.out.println(getExtName("file/of/Path/fileName-dev"));
        System.out.println(getExtName("file/of/Path.abc/fileName-dev.txt"));

        System.out.println(getFileName("file/of/Path/fileName.ext_name"));
        System.out.println(getFileName("file/of/Path/fileName"));
        System.out.println(getFileName("file/of/Path.abc/fileName.txt"));
        System.out.println(getFileName("fileName.ext_name"));
        System.out.println(getFileName("fileName"));
        System.out.println(getFileName("fileName.txt"));
        System.out.println(getFileName("file/of/Path/fileName-test.ext_name"));
        System.out.println(getFileName("file/of/Path/fileName-dev"));
        System.out.println(getFileName("file/of/Path.abc/fileName-dev.txt"));

        System.out.println(getFullPath("file/of/Path/fileName.ext_name"));
        System.out.println(getFullPath("file/of/Path/fileName"));
        System.out.println(getFullPath("file/of/Path.abc/fileName.txt"));
        System.out.println(getFullPath("fileName.ext_name"));
        System.out.println(getFullPath("fileName"));
        System.out.println(getFullPath("fileName.txt"));
        System.out.println(getFullPath("file/of/Path/fileName-test.ext_name"));
        System.out.println(getFullPath("file/of/Path/fileName-dev"));
        System.out.println(getFullPath("file/of/Path.abc/fileName-dev.txt"));

        System.out.println(getFullProfileName("prod", "file/of/Path/fileName.ext_name"));
        System.out.println(getFullProfileName("prod", "file/of/Path/fileName"));
        System.out.println(getFullProfileName("prod", "file/of/Path.abc/fileName.txt"));
        System.out.println(getFullProfileName("prod", "fileName.ext_name"));
        System.out.println(getFullProfileName("prod", "fileName"));
        System.out.println(getFullProfileName("prod", "fileName.txt"));
        System.out.println(getFullProfileName("prod", "file/of/Path/fileName-test.ext_name"));
        System.out.println(getFullProfileName("prod", "file/of/Path/fileName-dev"));
        System.out.println(getFullProfileName("prod", "file/of/Path.abc/fileName-dev.txt"));

        System.out.println(getFileProfileName("prod", "file/of/Path/fileName.ext_name"));
        System.out.println(getFileProfileName("prod", "file/of/Path/fileName"));
        System.out.println(getFileProfileName("prod", "file/of/Path.abc/fileName.txt"));
        System.out.println(getFileProfileName("prod", "fileName.ext_name"));
        System.out.println(getFileProfileName("prod", "fileName"));
        System.out.println(getFileProfileName("prod", "fileName.txt"));
        System.out.println(getFileProfileName("prod", "file/of/Path/fileName-test.ext_name"));
        System.out.println(getFileProfileName("prod", "file/of/Path/fileName-dev"));
        System.out.println(getFileProfileName("prod", "file/of/Path.abc/fileName-dev.txt"));

        System.out.println(getFileProfileName("", "file/of/Path/fileName.ext_name"));
        System.out.println(getFileProfileName("", "file/of/Path/fileName"));
        System.out.println(getFileProfileName("", "file/of/Path.abc/fileName.txt"));
        System.out.println(getFileProfileName("", "fileName.ext_name"));
        System.out.println(getFileProfileName("", "fileName"));
        System.out.println(getFileProfileName("", "fileName.txt"));
        System.out.println(getFileProfileName("", "file/of/Path/fileName-test.ext_name"));
        System.out.println(getFileProfileName("", "file/of/Path/fileName-dev"));
        System.out.println(getFileProfileName("", "file/of/Path.abc/fileName-dev.txt"));

        System.out.println(getFullProfileName("", "file/of/Path/fileName.ext_name"));
        System.out.println(getFullProfileName("", "file/of/Path/fileName"));
        System.out.println(getFullProfileName("", "file/of/Path.abc/fileName.txt"));
        System.out.println(getFullProfileName("", "fileName.ext_name"));
        System.out.println(getFullProfileName("", "fileName"));
        System.out.println(getFullProfileName("", "fileName.txt"));
        System.out.println(getFullProfileName("", "file/of/Path/fileName-test.ext_name"));
        System.out.println(getFullProfileName("", "file/of/Path/fileName-dev"));
        System.out.println(getFullProfileName("", "file/of/Path.abc/fileName-dev.txt"));


        System.out.println(getProfileName("file/of/Path/fileName.ext_name"));
        System.out.println(getProfileName("file/of/Path/fileName"));
        System.out.println(getProfileName("file/of/Path.abc/fileName.txt"));
        System.out.println(getProfileName("fileName.ext_name"));
        System.out.println(getProfileName("fileName"));
        System.out.println(getProfileName("fileName.txt"));
        System.out.println(getProfileName("file/of/Path/fileName-test.ext_name"));
        System.out.println(getProfileName("file/of/Path/fileName-dev"));
        System.out.println(getProfileName("file/of/Path.abc/fileName-dev.txt"));
    }
*/
}
